<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="系统模块" prop="title">
        <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable style="width: 240px;"
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="操作人员" prop="operName">
        <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable style="width: 240px;"
          @keyup.enter.native="handleQuery" />
      </el-form-item>
      <el-form-item label="类型" prop="businessType">
        <el-select v-model="queryParams.businessType" placeholder="操作类型" clearable style="width: 240px">
          <el-option v-for="dict in dict.type.sys_oper_type" :key="dict.value" :label="dict.label"
            :value="dict.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="queryParams.status" placeholder="操作状态" clearable style="width: 240px">
          <el-option v-for="dict in dict.type.sys_common_status" :key="dict.value" :label="dict.label"
            :value="dict.value" />
        </el-select>
      </el-form-item>
      <el-form-item label="操作时间">
        <el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
          range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
          :default-time="['00:00:00', '23:59:59']"></el-date-picker>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8">
      <!-- <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['monitor:operlog:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          @click="handleClean"
          v-hasPermi="['monitor:operlog:remove']"
        >清空</el-button>
      </el-col> -->
      <el-col :span="1.5">
        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
          v-hasPermi="['monitor:operlog:export']">导出
        </el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <el-table ref="tables" v-loading="loading" :data="list" @selection-change="handleSelectionChange"
      :default-sort="defaultSort" @sort-change="handleSortChange">
      <el-table-column type="selection" width="50" align="center" />
      <el-table-column label="日志编号" align="center" prop="operId" />
      <el-table-column label="系统模块" align="center" prop="title" :show-overflow-tooltip="true" />
      <el-table-column label="操作类型" align="center" prop="businessType">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_oper_type" :value="scope.row.businessType" />
        </template>
      </el-table-column>
      <el-table-column label="操作人员" align="center" prop="operName" width="110" :show-overflow-tooltip="true"
        sortable="custom" :sort-orders="['descending', 'ascending']" />
      <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
      <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
      <el-table-column label="操作状态" align="center" prop="status">
        <template slot-scope="scope">
          <dict-tag :options="dict.type.sys_common_status" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="操作日期" align="center" prop="operTime" width="160" sortable="custom"
        :sort-orders="['descending', 'ascending']">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.operTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="消耗时间" align="center" prop="costTime" width="110" :show-overflow-tooltip="true"
        sortable="custom" :sort-orders="['descending', 'ascending']">
        <template slot-scope="scope">
          <span>{{ scope.row.costTime }}毫秒</span>
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
        <template slot-scope="scope">
          <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row, scope.index)"
            v-hasPermi="['monitor:operlog:query']">详细
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
      @pagination="getList" />

    <!-- 操作日志详细 -->
    <el-dialog title="操作日志详细" :visible.sync="open" width="1000px" append-to-body>
      <div class="operation-detail-container">
        <!-- 固定基础信息 -->
        <div class="fixed-base-info">
          <el-descriptions :column="3" border>
            <el-descriptions-item label="操作人">
              <span class="highlight-text">{{ baseInfo.operator }}</span>
            </el-descriptions-item>
            <el-descriptions-item label="操作模块">
              {{ baseInfo.module }}
            </el-descriptions-item>
            <el-descriptions-item label="操作时间">
              {{ baseInfo.operTime }}
            </el-descriptions-item>
            <el-descriptions-item label="操作描述">
              <span>{{ baseInfo.operDesc }}</span>
            </el-descriptions-item>
            <el-descriptions-item label="异常信息">
              <span>{{ baseInfo.errorMsg }}</span>
            </el-descriptions-item>
          </el-descriptions>
        </div>

        <!-- 单据头变更 -->
        <div class="change-section">
          <h3 class="section-title">
            <el-icon>
              <document />
            </el-icon>
            单据头变更
          </h3>
          <el-table :data="this.getHeaderChanges(this.data.operationDetail)" border size="small"
            empty-text="操作未产生数据变更或不涉及单据头数据">
            <el-table-column prop="field" label="变更字段" width="200">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.field }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更前值" width="240">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.oldValue || '-' }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更后值" width="240">
              <template slot-scope="{ row }">
                <span class="field-name">{{ row.newValue || '-' }}</span>
              </template>
            </el-table-column>
            <el-table-column label="变更类型">
              <template slot-scope="{ row }">
                <el-tag size="small">{{ row.changeType }}</el-tag>
              </template>
            </el-table-column>
          </el-table>
        </div>

        <!-- 明细行变更 -->
        <div class="change-section">
          <h3 class="section-title">
            <el-icon>
              <list />
            </el-icon>
            明细变更
          </h3>
          <!-- 判断是否有数据 -->
          <template v-if="getDetailChanges(this.data.detailDesc).length > 0">
            <el-collapse accordion>
              <el-collapse-item v-for="(line, index) in getDetailChanges(this.data.detailDesc)" :key="index"
                :name="line.lineNumber">
                <template #title>
                  <span class="line-title">行号: {{ line.lineNumber }}</span>
                  <el-tag size="small">{{ line.changes.length }}项变更</el-tag>
                </template>
                <el-table :data="line.changes" border size="small" empty-text="未检测到变更">
                  <el-table-column prop="field" label="变更字段" width="200">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.field }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更前值" width="240">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.oldValue || '-' }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更后值" width="240">
                    <template slot-scope="{ row }">
                      <span class="field-name">{{ row.newValue || '-' }}</span>
                    </template>
                  </el-table-column>
                  <el-table-column label="变更类型">
                    <template slot-scope="{ row }">
                      <el-tag size="small">{{ row.changeType }}</el-tag>
                    </template>
                  </el-table-column>
                </el-table>
              </el-collapse-item>
            </el-collapse>
          </template>
          <!-- 没有数据时显示提示 -->
          <template v-else>
            <el-alert type="info" :closable="false" description="操作未产生数据变更或不涉及明细数据" show-icon />
          </template>
        </div>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="open = false">关 闭</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { list, delOperlog, cleanOperlog } from "@/api/monitor/operlog";

// 查询操作日志详情
import { listId } from "@/api/monitor/operlog";

// 浏览器中对网页进行截图，将这些截图转换为图像格式
import html2canvas from 'html2canvas';
// 生成 PDF 的库
import jsPDF from 'jspdf';
import 'jspdf-autotable';

export default {
  name: "Operlog",
  dicts: ['sys_oper_type', 'sys_common_status'],
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 表格数据
      list: [],
      // 是否显示弹出层
      open: false,
      // 日期范围
      dateRange: [],
      // 默认排序
      defaultSort: { prop: 'operTime', order: 'descending' },
      // 表单参数
      form: {},
      // 接收详情请求到数据
      data: {},
      // 详情显示基础数据
      baseInfo: {
        operator: '',
        operTime: '',
        method: '',
        apiPath: '',
        cost: '',
        operDesc: '' // 新增操作描述字段
      },
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        title: undefined,
        operName: undefined,
        businessType: undefined,
        status: undefined
      }
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** 查询登录日志 */
    getList() {
      this.loading = true;
      this.open = false; // 确保对话框关闭
      list(this.addDateRange(this.queryParams, this.dateRange)).then(response => {
        this.list = response.rows;
        this.total = response.total;
        this.loading = false;
      }
      );
    },

    // 操作日志类型字典翻译
    typeFormat(row, column) {
      return this.selectDictLabel(this.dict.type.sys_oper_type, row.businessType);
    },

    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },

    /** 重置按钮操作 */
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.queryParams.pageNum = 1;
      this.$refs.tables.sort(this.defaultSort.prop, this.defaultSort.order)
    },

    /** 多选框选中数据 */
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.operId)
      this.multiple = !selection.length
    },

    /** 排序触发事件 */
    handleSortChange(column, prop, order) {
      this.queryParams.orderByColumn = column.prop;
      this.queryParams.isAsc = column.order;
      this.getList();
    },

    /** 详细按钮操作 */
    handleView(row) {
      this.open = true;
      // this.form = row;

      let id = row.operId;

      this.loading = true;
      listId(id).then(response => {
        if (response && response.data) {
          this.data = response.data;
          console.log("listId=", response.data)
          this.parseBasicInfo();
        } else {
          console.error('响应数据结构不符合预期:', response);
        }
        this.loading = false;
      }).catch(error => {
        console.error('请求出错:', error);
        this.loading = false;
      });
    },

    /** 删除按钮操作 */
    handleDelete(row) {
      const operIds = row.operId || this.ids;
      this.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项？').then(function () {
        return delOperlog(operIds);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {
      });
    },

    /** 清空按钮操作 */
    handleClean() {
      this.$modal.confirm('是否确认清空所有操作日志数据项？').then(function () {
        return cleanOperlog();
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("清空成功");
      }).catch(() => {
      });
    },
    /** 导出按钮操作 */
    // handleExport() {
    //   this.download('monitor/operlog/export', {
    //     ...this.queryParams
    //   }, `operlog_${new Date().getTime()}.xlsx`)
    // }

    /** 导出按钮操作(初步2，成功导出PDF，导出内容加10px边框) */
    // async handleExport() {
    //   const table = this.$refs.tables.$el;
    //   const canvas = await html2canvas(table);
    //   const imgData = canvas.toDataURL('image/png');

    //   const doc = new jsPDF();
    //   const imgProps = doc.getImageProperties(imgData);
    //   const pdfWidth = doc.internal.pageSize.getWidth();
    //   const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;

    //   // 添加 10px 的边距
    //   const margin = 10;
    //   const x = margin;
    //   const y = margin;
    //   const newWidth = pdfWidth - 2 * margin;
    //   const newHeight = (imgProps.height * newWidth) / imgProps.width;

    //   doc.addImage(imgData, 'PNG', x, y, newWidth, newHeight);
    //   doc.save(`logininfor_${new Date().getTime()}.pdf`);
    // },
    /** 导出按钮操作 */
    handleExport() {
      this.download('/monitor/order/operlog/export', {
        ...this.queryParams
      }, `单据操作日志_${this.formatTime(new Date())}.pdf`)
    },

    // 核心解析方法
    parseChangeData(rawData) {
      try {
        const data = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
        return Object.entries(data).map(([key, value]) => {
          // 判断是否为日期字段
          const isDateField = (field) => field.includes('日期') || field.includes('时间');

          // 日期格式化处理（移除末尾的.0）
          const formatDate = (val) => {
            if (typeof val === 'string' && isDateField(key)) {
              return val.replace(/\.\d+$/, ''); // 移除末尾的.和数字
            }
            return val;
          };

          // 提取并格式化新旧值
          const oldVal = value.oldValue !== undefined ? formatDate(value.oldValue) : null;
          const newVal = value.newValue !== undefined ? formatDate(value.newValue) : null;

          // 判断变更类型
          let changeType = '数值变更';
          if (value.oldValue === undefined || value.oldValue === null) {
            changeType = '新增字段';
          } else if (value.newValue === undefined || value.newValue === null) {
            changeType = '删除字段';
          }

          return {
            field: this.mapFieldName(key), // 保留字段名映射逻辑
            oldValue: oldVal,
            newValue: newVal,
            changeType: changeType
          };
        });
      } catch (e) {
        return [];
      }
    },

    // 解析详情显示基础信息
    parseBasicInfo() {
      this.baseInfo = {
        operator: this.data.operName || '未知',
        operTime: this.formatTime(this.data.operTime),
        method: this.data.requestMethod || '-',
        apiPath: this.data.operUrl || '-',
        cost: `${this.data.costTime}ms`,
        operDesc: this.data.operDesc || '无描述',
        errorMsg: this.data.errorMsg || '无错误信息',// 新增操作描述解析
        module: this.data.title + "/" + this.typeFormat(this.data)
      }
    },

    // 解析表头变更数据
    getHeaderChanges(rawData) {
      try {
        if (rawData == null || rawData === '') {
          return [];
        }
        const data = typeof rawData === 'string' ? JSON.parse(rawData) : rawData;
        const headerChanges = [];

        Object.entries(data).forEach(([key, value]) => {
          // 忽略行号数据（明细数据）
          if (key.startsWith('行号') || key.includes('行号：')) return;

          // 处理表头字段
          const change = this.processField(key, value);
          headerChanges.push(change);
        });

        return headerChanges;
      } catch (e) {
        console.error('表头数据解析错误:', e);
        return [];
      }
    },

    // 解析明细数据
    getDetailChanges(detailDesc) {
      try {
        if (detailDesc == null || detailDesc === '') {
          return [];
        }
        const lineChanges = [];

        // 遍历数组中的每个字符串
        detailDesc.forEach((item) => {
          // 解析字符串为对象
          const data = JSON.parse(item);

          // 处理每行数据
          Object.entries(data).forEach(([key, value]) => {
            // 提取行号
            const lineNumber = key.match(/\d+/)?.[0] || '未知行号';

            // 处理行内字段变更
            const changes = Object.entries(value).map(([fieldKey, fieldValue]) => {
              return this.processField(fieldKey, fieldValue);
            });

            lineChanges.push({
              lineNumber: lineNumber,
              changes: changes
            });
          });
        });

        return lineChanges;
      } catch (e) {
        console.error('明细数据解析错误:', e);
        return [];
      }
    },

    // 通用字段处理逻辑
    processField(key, value) {
      const isDateField = (k) => k.includes('日期') || k.includes('时间');
      const formatValue = (val, k) =>
        typeof val === 'string' && isDateField(k) ? val.replace(/\.\d+$/, '') : val;

      // 判断变更类型
      let changeType = '数值变更';
      if (value.oldValue == null) changeType = '新增字段';
      if (value.newValue == null) changeType = '删除字段';

      return {
        field: this.mapFieldName(key), // 字段名映射
        oldValue: value.oldValue !== undefined ? formatValue(value.oldValue, key) : null,
        newValue: value.newValue !== undefined ? formatValue(value.newValue, key) : null,
        changeType: changeType
      };
    },

    // 获取每行的明细变更描述
    getDetailDesc(item) {
      const isLineKey = item.startsWith('行号') || item.includes('行号：');
      if (isLineKey) {
        // 明细行处理逻辑
        Object.entries(item).forEach(([key, value]) => {
          return value
        })
      }
      return item;
    },

    // 扁平化嵌套数据结构
    flattenData(obj, prefix = '') {
      return Object.entries(obj).reduce((acc, [key, value]) => {
        const fullKey = prefix ? `${prefix}.${key}` : key
        if (typeof value === 'object' && !Array.isArray(value)) {
          Object.assign(acc, this.flattenData(value, fullKey))
        } else {
          acc[fullKey] = value
        }
        return acc
      }, {})
    },

    // 解析值变更
    parseValueChange(value) {
      const strVal = String(value)
      // 匹配格式：由 [旧值] 变更为 [新值]
      const standardMatch = strVal.match(/由 $$(.*?)$$ 变更为 $$(.*?)$$/)
      if (standardMatch) return [standardMatch[1], standardMatch[2], '标准变更']

      // 匹配新增：新增值 [值]
      const addMatch = strVal.match(/新增值 $$(.*?)$$/)
      if (addMatch) return [null, addMatch[1], '新增字段']

      // 匹配删除：删除值 [值]
      const delMatch = strVal.match(/删除值 $$(.*?)$$/)
      if (delMatch) return [delMatch[1], null, '删除字段']

      // 默认处理
      return [strVal, strVal, '未知变更']
    },

    // 字段名称映射
    mapFieldName(field) {
      const fieldMap = {
        'lineNumber': '行号'
      }
      return fieldMap[field] || this.formatFieldName(field)
    },

    // 格式字段名称（处理驼峰等）
    formatFieldName(name) {
      return name
        .replace(/([A-Z])/g, ' $1')
        .replace(/_/g, ' ')
        .replace(/\./g, ' / ')
        .toLowerCase()
        .replace(/(^|\s)\S/g, l => l.toUpperCase())
    },

    // 解析行号
    parseLineNumber(item) {
      try {
        const json = JSON.parse(item)
        const key = Object.keys(json)[0]
        return key.match(/行号：?(\d+)/)?.[1] || '未知'
      } catch {
        return '未知'
      }
    },

    // 时间格式化
    formatTime(cellValue) {
      // 获取单元格数据
      let dtc = new Date(cellValue)
      //获取月,默认月份从0开始
      let dtuMonth = dtc.getMonth() + 1
      //获取日
      let dtuDay = dtc.getDate()
      //处理1-9月前面加0
      if (dtuMonth < 10) {
        dtuMonth = "0" + (dtc.getMonth() + 1)
      }
      //处理1-9天前面加0
      if (dtuDay < 10) {
        dtuDay = "0" + dtc.getDate()
      }
      //获取小时
      let dtuHours = dtc.getHours()
      //处理1-9时前面加0
      if (dtuHours < 10) {
        dtuHours = "0" + dtc.getHours()
      }
      //获取分钟
      let dtuMinutes = dtc.getMinutes()
      //处理1-9分前面加0
      if (dtuMinutes < 10) {
        dtuMinutes = "0" + dtc.getMinutes()
      }
      //获取秒
      let dtuSeconds = dtc.getSeconds()
      //处理1-9秒前面加0
      if (dtuSeconds < 10) {
        dtuSeconds = "0" + dtc.getSeconds()
      }
      //组装年月日时分秒,按自己的要求来
      return dtc.getFullYear() + "-" + dtuMonth + "-" + dtuDay + " " + dtuHours + ":" + dtuMinutes + ":" + dtuSeconds
    },

    // 处理耗时百分比（示例）
    timePercent() {
      return Math.min(100, Math.floor((this.data.costTime || 0) / 5))
    },

    // 请求方法样式
    methodType() {
      return this.data.requestMethod === 'POST' ? 'success' : ''
    }
  }
};
</script>

<style scoped>
.operation-detail-container {
  padding: 0 15px;
}

/* .fixed-base-info {
  margin-bottom: 25px;

  :deep(.el-descriptions__body) {
    background: #f8f9fa;
  }
} */
.fixed-base-info {
  margin-bottom: 25px;
}

/* 使用 /deep/ */
.fixed-base-info /deep/ .el-descriptions__body {
  background: #f8f9fa;
}

.change-section {
  margin: 25px 0;
}

.section-title {
  display: flex;
  align-items: center;
  color: #606266;
  font-size: 16px;
  margin: 18px 0;
}

.el-icon {
  margin-right: 8px;
  font-size: 18px;
}

.line-title {
  margin-right: 12px;
  font-weight: 500;
}

.highlight-text {
  color: #409eff;
  font-weight: 500;
}

.field-name {
  font-weight: 500;
  color: #303133;
}

:deep(.el-collapse-item__header) {
  font-weight: 500;
  background: #f8f9fa;
}

:deep(.el-progress-bar) {
  padding-right: 70px;
}
</style>
